package roborally.model;

import java.io.IOException;
import java.io.Writer;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import roborally.*;
import roborally.Exceptions.IllegalAmountException;

/**
 * An interface that implements IFacade and makes connection between IRobots and the methods in the class Robot.java.
 * 
 * @version 1.36
 * @author Michiel Ruelens & Dieter Naten
 */

public class Facade implements IFacade<Board, Robot, Wall, Battery, RepairKit, SurpriseBox> {
	
	@Override
	public Board createBoard(long width, long height){
		Board board = new Board(width, height);
		return board;
	}
	
	@Override
	public void merge(Board board1, Board board2) {
		board1.merge(board2);
	}
	
	@Override
	public Battery createBattery(double initialEnergy, int weight){
		Battery battery = new Battery(initialEnergy,weight);
		return battery;
	}
	
	@Override
	public void putBattery(Board board, long x, long y, Battery battery){
		battery.putBattery(board, x, y);
		board.addPlaceable(battery);
	}
	
	@Override
	public long getBatteryX(Battery battery) throws IllegalStateException{
		return battery.getX();
		
	}
	
	@Override
	public long getBatteryY(Battery battery) throws IllegalStateException{
		return battery.getY();
		
	}
	
	@Override
	public Robot createRobot(int orientation, double energy) {
		return new Robot(orientation,energy);
		
	}
	
	@Override
	public void putRobot(Board board, long x, long y, Robot robot){
		robot.putRobot(board, x, y);
		board.addPlaceable(robot);
	}

	@Override
	public long getRobotX(Robot robot) {
		return robot.getX();
	}

	@Override
	public long getRobotY(Robot robot) {
		return robot.getY();
	}

	@Override
	public int getOrientation(Robot robot) {
		return robot.getOrientation();
	}

	@Override
	public double getEnergy(Robot robot) {
		return robot.getEnergy();
	}
	
	@Override
	public void move(Robot robot) {
		try{
		robot.move();
		}
		catch(IllegalAmountException exc){
			Robot excRobot = exc.getRobot();
			assert(!Coordinate.isValidXCoordinate(excRobot.getBoard(),excRobot.getX()+exc.getAmount()) 
				|| !Coordinate.isValidYCoordinate(excRobot.getBoard(),excRobot.getY()+exc.getAmount()));
			System.out.println("The destination coordinate is not a valid one. Leaving the board is not allowed.");
			//Nothing else will be done as the move is not executed.
			}
	}

	@Override
	public void turn(Robot robot) {
		robot.turnClockwise();
	}

//	@Override
//	public Set<Battery> getPossessions(Robot robot){
//		Set<Battery> resultSet = (Set<Battery>) robot.getAllPossessionsByType(PlaceableType.BATTERY);
//		return resultSet;
//	}
//
//	@Override
//	public void pickUp(Robot robot, Battery battery){
//		robot.pickUp(battery);
//		
//	}
//	
//	@Override
//	public void use(Robot robot, Battery battery){
//		robot.useCarryable(battery);
//	}
//	
//	@Override
//	public void drop(Robot robot, Battery battery){
//		robot.drop(battery);
//	}

	@Override
	public int isMinimalCostToReach17Plus(){
		return 1;
		
	}
	
	@Override
	public double getMinimalCostToReach(Robot robot, long x, long y){
		return robot.getEnergyRequiredToReach(x, y);
	}
	
	@Override
	public int isMoveNextTo18Plus(){
		return 1;
		
	}
	
	@Override
	public void shoot(Robot robot) throws UnsupportedOperationException{
		robot.shoot();
	}
//	public void recharge(IRobot robot, double energyAmount) {
//		robot.recharge(energyAmount);
//	}
//
//	public int isGetEnergyRequiredToReachAndMoveNextTo16Plus() {
//		return 0;
//	}
//
//	public int isGetEnergyRequiredToReachAndMoveNextTo16Plus(IRobot robot, IRobot robot2) {
//		int energy = robot.isGetEnergyRequiredToReachAndMoveNextTo16Plus();
//		return energy;
//	}
//
//	public double getEnergyRequiredToReach(IRobot robot, long x, long y) {
//		double energyRequired = robot.getEnergyRequiredToReach(x, y);
//		return energyRequired;
//	}

	@Override
	public void moveNextTo(Robot robot, Robot robot2) {
	    robot.moveNextTo(robot, robot2);
	}
	
	@Override
	public Wall createWall() throws UnsupportedOperationException{
		Wall wall = new Wall();
		return wall;		
	}
	
	@Override
	public void putWall(Board board, long x, long y, Wall wall) throws UnsupportedOperationException{
		wall.putWall(board, x, y);
		board.addPlaceable(wall);
	}
	
	@Override
	public long getWallX(Wall wall) throws IllegalStateException, UnsupportedOperationException{
		return wall.getX();
	}
	
	@Override
	public long getWallY(Wall wall) throws IllegalStateException, UnsupportedOperationException{
		return wall.getY();
		
	}
	
	@Override
	public Set<Robot> getRobots(Board board){
		return (Set<Robot>) board.getAllObjectsByType(PlaceableType.ROBOT);
		
	}
	
	@Override
	public Set<Battery> getBatteries(Board board){
		return (Set<Battery>) board.getAllObjectsByType(PlaceableType.BATTERY);
		
	}
	
	@Override
	public Set<Wall> getWalls(Board board) throws UnsupportedOperationException{
		return (Set<Wall>) board.getAllObjectsByType(PlaceableType.WALL);
	}

	@Override
	public RepairKit createRepairKit(double repairAmount, int weight) {
		return new RepairKit(repairAmount,weight);
	}

	@Override
	public void putRepairKit(Board board, long x, long y, RepairKit repairKit) {
		repairKit.putRepairKit(board, x, y);
	}

	@Override
	public long getRepairKitX(RepairKit repairKit) throws IllegalStateException {
		return repairKit.getX();
	}

	@Override
	public long getRepairKitY(RepairKit repairKit) throws IllegalStateException {
		return repairKit.getY();
	}

	@Override
	public SurpriseBox createSurpriseBox(int weight) {
		return new SurpriseBox(weight);
	}

	@Override
	public void putSurpriseBox(Board board, long x, long y,
			SurpriseBox surpriseBox) {
		surpriseBox.putSurpriseBox(board, x, y);
		
	}

	@Override
	public long getSurpriseBoxX(SurpriseBox surpriseBox)
			throws IllegalStateException {
		return surpriseBox.getX();
	}

	@Override
	public long getSurpriseBoxY(SurpriseBox surpriseBox)
			throws IllegalStateException {
		return surpriseBox.getY();
	}

	@Override
	public void pickUpBattery(Robot robot, Battery battery) {
		robot.pickUp(battery);
	}

	@Override
	public void useBattery(Robot robot, Battery battery) {
		robot.useCarryable(battery);
	}

	@Override
	public void dropBattery(Robot robot, Battery battery) {
		robot.drop(battery);
	}

	@Override
	public void pickUpRepairKit(Robot robot, RepairKit repairKit) {
		robot.pickUp(repairKit);
	}

	@Override
	public void useRepairKit(Robot robot, RepairKit repairKit) {
		robot.useCarryable(repairKit);
	}

	@Override
	public void dropRepairKit(Robot robot, RepairKit repairKit) {
		robot.drop(repairKit);
	}

	@Override
	public void pickUpSurpriseBox(Robot robot, SurpriseBox surpriseBox){
		robot.pickUp(surpriseBox);
	}

	@Override
	public void useSurpriseBox(Robot robot, SurpriseBox surpriseBox) {
		robot.useCarryable(surpriseBox);
	}

	@Override
	public void dropSurpriseBox(Robot robot, SurpriseBox surpriseBox) {
		robot.drop(surpriseBox);
	}

	@Override
	public void transferItems(Robot from, Robot to) {
		from.transfersCarryables(to);
		
	}

	@Override
	public Set<RepairKit> getRepairKits(Board board) {
		return (Set<RepairKit>)board.getAllObjectsByType(PlaceableType.REPAIRKIT);
	}

	@Override
	public Set<SurpriseBox> getSurpriseBoxes(Board board) {
		return (Set<SurpriseBox>)board.getAllObjectsByType(PlaceableType.SURPRISEBOX);
	}

	@Override
	public int loadProgramFromFile(Robot robot, String path) {
		return robot.loadProgramFromFile(path);
	}

	@Override
	public int saveProgramToFile(Robot robot, String path) {
		return robot.saveProgramToFile(path);
	}

	@Override
	public void prettyPrintProgram(Robot robot, Writer writer) {
		Program program = robot.getProgram();
		List<Statement> statementList = program.getStatementList();
		for(Statement s: statementList){
			System.out.println(s.getStringForm());
		}
	}

	@Override
	public void stepn(Robot robot, int n) {
		int i = 1;
		while(i<=n){
			robot.executeOneStepOfProgram();
		}
	}
	
}
